第5週 関数
前回の復習
省略
入力した文字列を逆順に出力するプログラムを書いてください。
code:reverse.c
int main() {
fgets(input, sizeof(input), stdin); //fgets(読み取った値を格納する配列,行の最大文字数,ストリーム(どこから値を読み取るか))
inputstrlen(input)-1 = '\0'; //strlen(文字列)文字列のながさを取得,fgets関数は最後のenterも読み取るので'\n'が入ってしまい 、それを'\0'に変えてる for(int i=strlen(input)-1; i>=0; i--) {
}
puts("");
}
入力した文字列の中の'a'だけを'*'に変換して出力するプログラムを書いてください。
code:asterisk.c
int main() {
fgets(input, sizeof(input), stdin);
for(int i=0; i<strlen(input); i++) {
if(inputi=='a') putc('*', stdout); else putc(inputi, stdout); }
puts("");
}
関数とは
プログラム中でひとまとまりの処理部分、繰り返し使う時などに使用する
プログラム中の処理をまとめ、名前をつけたもの。
その処理に使用する変数を引数として受け取り、その結果などを返り値として返す。
関数定義
戻り値の型 関数名(引数の型 引数){処理内容}
値渡し
関数内部に引数として値をわたせるよ。
code:value.c
void HOGE( int a )
{
a = 34;
}
int main()
{
int a = 12;
HOGE( a ); //値渡し
printf("a = %d\n", a );
return 0;
}
これの出力結果はa = 12となる。main関数で指定したaはそのまま保持される。HOGE関数のaは34を保持してる。このように渡されて関数で変数を変更しても呼び出し元の変数は変わらない。
プロトタイプ宣言、includeヘッダ
プロトタイプ宣言は、コンパイラに対して関数の情報を与えるためのものです。プロトタイプ宣言は省略可能ですが、記述しておくと、コンパイル時に関数の呼び出しと定義が異なっている場合にはエラーを出力してくれます。プロトタイプ宣言をしないとmain関数の後に関数を定義してるとコンパイルエラーを吐き出します。
記法戻り値の型 関数名(引数の型);
code:value.c
void HOGE(int); //プロトタイプ宣言
void HOGE( int a )
{
a = 34;
}
int main()
{
int a = 12;
HOGE( a ); //値渡し
printf("a = %d\n", a );
return 0;
}
include ヘッダは他のファイルの内容を展開するよ。
一πん的にC言語ではヘッダファイルとソースファイルの2種類があり、前者は~.hというファイルで後者は~.cといファイルでエス。
今まで呪文のように唱えていた#include <stdio.h>はstdio.hというヘッダーファイルを展開してるということでエス
このファイルどこにあるの?→/usr/include/stdio.hとかにあります。人によっては場所ちゃうかも。まあでもちゃんとどっかにあります。
スコープ・グローバル変数
スコープとは・・・変数の有効範囲
グローバル変数・・・関数の外側で宣言されプログラム全体に有効な変数
code:scope.c
int d = 5;
int main() {
int d = 3;
printf("%d\n", d);
}
static指定子
static = 静的。対義語は dynamic (動的)
まず以下を実行してみましょう
code:static.c
int func(int tmp){
int x;
x+=tmp;
return x;
}
int main(){
int result;
result = func(5);
printf("%d\n",result);
result = func(10);
printf("%d\n",result);
return 0;
}
5
32538(不定値) とこんな感じの出力になります。int型の変数宣言はデフォルトで0になっているが、それ以降の変数宣言では値は保証されない
次に
code:static.c
int func(int tmp){
//スタティック変数にする
static int x;
x+=tmp;
return x;
}
int main(){
int result;
result = func(5);
printf("%d\n",result);
result = func(10);
printf("%d\n",result);
return 0;
}
こうすると出力が
5
15になり前のxが記憶されていることがわかります。
余談:プログラム実行時のメモリ配置について
プログラムを実行する時以下のようなメモリ配置になっています。
https://gyazo.com/8ed5cf4401d1ec095f224ce410999b8d
関数内(main関数も含む)で用いる変数は基本的にスタック領域に配置され、関数が終了すると次の関数呼び出しなどによって上書きされてしまいます。
staticを指定した変数はスタック領域に配置される代わりに静的領域に配置されます。この領域に存在する変数はプログラムが終了するまでと同じ寿命を持ち、値が上書きされることはありません。
再帰呼び出し
再帰を理解するためには、再帰を学んでください。半永久機関的事象変化(ナイトオブディスパイアー)
ある関数が関数内で自身を呼び出してる時に、再帰呼び出しという。
例)階乗
code:saiki.c
int fact(int n){
int m;
if (n == 0)
return 1; // 0! = 1
m = fact(n - 1); // (n-1) を求めてそれを m とおく。ここのfact(n-1)が再帰呼出し。
return n * m; // n! = n * m
}
int main(){
int i, ans;
scanf("%d", &i);
ans = fact(i);
printf("%dの階乗は%d.\n", i, ans);
return 0;
}
練習問題
2値x,yを受け取って√(x^2+y^2)を返す関数my_hypotを書いてください
プロトタイプ宣言例:float my_hypot(float, float);
math.hに引数の平方根を返す関数sqrtが書かれています
math.hをincludeしたソースコードをコンパイルするときはオプションに-lmを渡します
gcc -lm hypot.c
再帰でn番目のフィボナッチ数を求める関数fibを書いてください
プロトタイプ宣言例:int fib(int n);
挑戦問題